/**
 * Package	: com.adams.utils
 * File		: ImageUtil.java
 * Version  : v1.0
 * Description 	: <Detail Description for package>
 *
 * Create Date	: 2010-11-5
 * Create By    : 904004
 * History :$
 */
package com.adams.utils;

import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;

import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;

import org.apache.commons.lang.StringUtils;

/**
 * 
 * File Name : ImageUtil.java
 * 
 * @Description : Image 工具类</br>主要用于按指定大小截图
 */
public class ImageUtil
{

	/**
	 * Description : <Detail Description for method>
	 * 
	 * @param args
	 * 
	 */
	public static void main(String[] args)
	{
		try
		{
			compressImage(
					new File("F://20110406//in//201103280004570089c.jpg"),
					new File("F://20110406//out//201103280004570089c.jpg"),
					0.5f);
			saveAndCutImage(new File(
					"F://20110406//in//201103280004570089c.jpg"), new File(
					"F://20110406//out//201103280004570089c.jpg"), 561, 411,
					false);
			saveAndCutImage(new File(
					"F://20110406//in//201103280004570089c.jpg"), new File(
					"F://20110406//out//201103280004570089c(448_290).jpg"),
					448, 290, true);
		} catch (Exception e)
		{
			e.printStackTrace();
		}
	}

	/**
	 * 
	 * Description : 按比例缩放图片
	 * 
	 * @param source
	 *            要缩放的给定图片源
	 * @param targetW
	 *            给定宽度
	 * @param targetH
	 *            给定高度
	 * @return
	 * 
	 */
	public static BufferedImage resize(BufferedImage source, int targetW,
			int targetH, boolean proportion)
	{
		int type = source.getType();
		BufferedImage target;
		double sw = (double) targetW / source.getWidth();
		double sh = (double) targetH / source.getHeight();
		// 是否需要按比例缩放，若为false则按照给定的大小，这样会变形
		if (proportion)
		{
			if (sw > sh)
			{
				sh = sw;
				targetH = (int) (sh * source.getHeight());
			} else
			{
				sw = sh;
				targetW = (int) (sw * source.getWidth());
			}
		}

		if (type == BufferedImage.TYPE_CUSTOM)
		{
			ColorModel cm = source.getColorModel();
			WritableRaster raster = cm.createCompatibleWritableRaster(targetW,
					targetH);
			boolean alphaPremultiplied = cm.isAlphaPremultiplied();
			target = new BufferedImage(cm, raster, alphaPremultiplied, null);
		} else
		{
			target = new BufferedImage(targetW, targetH, type);
		}
		Graphics2D g = target.createGraphics();
		g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
				RenderingHints.VALUE_INTERPOLATION_BICUBIC);
		g.drawRenderedImage(source, AffineTransform.getScaleInstance(sw, sh));
		g.dispose();
		return target;
	}

	/**
	 * 
	 * Description : 按给定大小保存截图
	 * 
	 * @param inFilePath
	 *            图片源
	 * @param outFilePath
	 *            输出文件
	 * @param width
	 *            宽度
	 * @param hight
	 *            高度
	 * @param proportion
	 *            是否需要等比例缩放
	 * @throws Exception
	 * 
	 */
	public static void saveAndCutImage(File inFile, File outFile, int width,
			int hight, boolean proportion) throws Exception
	{
		saveAndCutImage(inFile, outFile, width, hight, proportion, 1f);
	}

	/**
	 * 
	 * Description : 按给定大小保存截图
	 * 
	 * @param inFilePath
	 *            图片源
	 * @param outFilePath
	 *            输出文件
	 * @param width
	 *            宽度
	 * @param hight
	 *            高度
	 * @param proportion
	 *            是否需要等比例缩放
	 * @param quality
	 *            压缩质量
	 * @throws Exception
	 * 
	 */
	public static void saveAndCutImage(File inFile, File outFile, int width,
			int hight, boolean proportion, float quality) throws Exception
	{
		InputStream in = new FileInputStream(inFile);
		File saveFile = outFile;

		BufferedImage srcImage = ImageIO.read(in);
		if (width > 0 || hight > 0)
		{
			// 原图的大小
			int sw = srcImage.getWidth();
			int sh = srcImage.getHeight();
			// 如果原图像的大小小于要缩放的图像大小，直接将要缩放的图像复制过去
			if (sw > width && sh > hight)
			{
				srcImage = resize(srcImage, width, hight, proportion);
			} else
			{
				compressImage(srcImage, saveFile, quality);
				if (saveFile.length() > inFile.length())
				{
					String fileName = saveFile.getName();
					String formatName = "";
					int pos = fileName.lastIndexOf('.');
					if (pos > -1)
					{
						formatName = fileName.substring(pos + 1).toLowerCase();
					}
					ImageIO.write(srcImage, formatName, outFile);
				}
				return;
			}
		}
		// 缩放后的图像的宽和高
		int w = srcImage.getWidth();
		int h = srcImage.getHeight();
		// 如果缩放后的图像和要求的图像宽度一样，就对缩放的图像的高度进行截取
		if (w == width)
		{
			// 计算X轴坐标
			int x = 0;
			int y = h / 2 - hight / 2;
			saveSubImage(srcImage, new Rectangle(x, y, width, hight), saveFile,
					quality);
		}
		// 否则如果是缩放后的图像的高度和要求的图像高度一样，就对缩放后的图像的宽度进行截取
		else if (h == hight)
		{
			// 计算X轴坐标
			int x = w / 2 - width / 2;
			int y = 0;
			saveSubImage(srcImage, new Rectangle(x, y, width, hight), saveFile,
					quality);
		}
		in.close();
	}

	/**
	 * 
	 * Description : 截图
	 * 
	 * @param image
	 *            需要截图的图片源
	 * @param subImageBounds
	 *            子图范围
	 * @param subImageFile
	 *            子图文件
	 * @param quality
	 *            压缩质量
	 * @throws IOException
	 * 
	 */
	private static void saveSubImage(BufferedImage image,
			Rectangle subImageBounds, File subImageFile, float quality)
			throws IOException
	{
		if (subImageBounds.x < 0 || subImageBounds.y < 0
				|| subImageBounds.width - subImageBounds.x > image.getWidth()
				|| subImageBounds.height - subImageBounds.y > image.getHeight())
		{
			System.out.println("Bad   subimage   bounds");
			return;
		}
		BufferedImage subImage = image.getSubimage(subImageBounds.x,
				subImageBounds.y, subImageBounds.width, subImageBounds.height);
		compressImage(subImage, subImageFile, quality);
	}

	/**
	 * 
	 * Description : 压缩图片 只对输出为jpeg类型的图片进行压缩，其它输出类型图片不进行压缩处理
	 * 
	 * @param inFile
	 *            输入图片文件
	 * @param outFile
	 *            输出图片文件
	 * @param quality
	 *            压缩质量
	 * @throws IOException
	 * 
	 */
	public static void compressImage(File inFile, File outFile, float quality)
			throws Exception
	{
		boolean needCompressFlag = true;
		String inFileName = inFile.getName();
		String inFormatName = "";
		int pos = inFileName.lastIndexOf('.');
		if (pos > -1)
		{
			inFormatName = inFileName.substring(pos + 1).toLowerCase();
		}
		if (StringUtils.isBlank(inFormatName))
		{
			throw new IllegalArgumentException("输入图片文件必须要有后缀名!");
		}
		String outFileName = outFile.getName();
		String outFormatName = "";
		pos = outFileName.lastIndexOf('.');
		if (pos > -1)
		{
			outFormatName = outFileName.substring(pos + 1).toLowerCase();
		}
		if (StringUtils.isBlank(outFormatName))
		{
			throw new IllegalArgumentException("输出图片文件必须要有后缀名!");
		}
		boolean jpgFlag = "jpg".equals(outFormatName)
				|| "jpeg".equals(outFormatName);
		// 相同文件类型，但输出不是jpg，直接图片复制
		if (outFormatName.equals(inFormatName) && !jpgFlag)
		{
			FileUtil.copy(inFile, outFile);
			return;
		}

		InputStream in = new FileInputStream(inFile);
		BufferedImage inImage = ImageIO.read(in);
		if (jpgFlag)
		{
			int sw = inImage.getWidth();
			int sh = inImage.getHeight();
			long inSize = inFile.length();
			needCompressFlag = ((sw * sh * 0.21) < inSize);
			if (needCompressFlag)
			{
				compressImage(inImage, outFile, quality);
				if (outFile.length() > inSize)
				{
					needCompressFlag = false;
				}
			}
		}
		if (!needCompressFlag)
		{
			ImageIO.write(inImage, outFormatName, outFile);
		}
		in.close();
	}

	/**
	 * 
	 * Description : 压缩图片 只对输出为jpeg类型的图片进行压缩，其它输出类型图片不进行压缩处理
	 * 
	 * @param image
	 *            图片源
	 * @param imageFile
	 *            输出图片文件
	 * @param quality
	 *            压缩质量
	 * @throws IOException
	 * 
	 */
	private static void compressImage(BufferedImage image, File imageFile,
			float quality) throws IOException
	{
		String fileName = imageFile.getName();
		String formatName = "";
		int pos = fileName.lastIndexOf('.');
		if (pos > -1)
		{
			formatName = fileName.substring(pos + 1).toLowerCase();
		}
		if (StringUtils.isBlank(formatName))
		{
			throw new IllegalArgumentException("输出图片文件必须要有后缀名!");
		}
		boolean jpg = ("jpg".equals(formatName) || "jpeg".equals(formatName));
		boolean needCompressFlag = 1.0f > quality;
		if (needCompressFlag && jpg)
		{
			ImageWriter writer = null;
			ImageTypeSpecifier type = ImageTypeSpecifier
					.createFromRenderedImage(image);
			Iterator<ImageWriter> iter = ImageIO.getImageWriters(type, "jpg");
			if (iter.hasNext())
			{
				writer = iter.next();
			}
			if (writer == null)
			{
				ImageIO.write(image, formatName, imageFile);
				return;
			}
			IIOImage iioImage = new IIOImage(image, null, null);
			ImageWriteParam param = writer.getDefaultWriteParam();
			param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
			param.setCompressionQuality(quality);
			ImageOutputStream outputStream = ImageIO
					.createImageOutputStream(imageFile);
			writer.setOutput(outputStream);
			writer.write(null, iioImage, param);
			writer.dispose();
			outputStream.flush();
			outputStream.close();
			return;
		}
		ImageIO.write(image, formatName, imageFile);
	}
}
